﻿#if NAMED_PIPES
#if NO_SILVERLIGHT
// From http://www.codeproject.com/KB/threads/dotnetnamedpipespart2.aspx?display=Print by Ivan L
using System;
using System.Threading;
using System.IO;
using System.Net;
using Ciloci.Flee;
using System.Collections;
using BrainTechLLC.ThreadSafeObjects;

using System.Collections.Generic;
using AppModule.InterProcessComm;
using AppModule.NamedPipes;

namespace BrainTechLLC
{
	public sealed class PipeManager : IChannelManager
	{
		public static PipeManager Manager;
		private static readonly uint OutBufferSize = 512;
		private static readonly uint InBufferSize = 512;
		private static readonly int MaxReadBytes = 5000;
		private static readonly int MaxPipeStuffedTime = 5000;
		private static readonly uint NumberPipes = 2;

		public event EventHandler<ObjectEventArgsWithReturn<string, string>> RequestReceived;
		public ThreadSafeLookup<int, ServerNamedPipe> Pipes;

		public bool Listen { get; set; }
		private int numChannels = 0;
		private Thread MainThread;
		private string PipeName = "MyPipe";
		private ManualResetEvent Mre;

		public void Initialize()
		{
			Pipes = new ThreadSafeLookup<int, ServerNamedPipe>();
			Mre = new ManualResetEvent(false);
			MainThread = new Thread(new ThreadStart(Start))
			{
				IsBackground = true,
				Name = "Main Pipe Thread"
			};
			MainThread.Start();
			Thread.Sleep(500);
		}

		public string HandleRequest(string request)
		{
			if (RequestReceived != null)
			{
				var args = new ObjectEventArgsWithReturn<string, string>(request);
				RequestReceived(this, args);
				return args.ReturnValue;
			}
			return string.Empty;
		}

		private void Start()
		{
			try
			{
				while (Listen)
				{
					int[] keys = new int[Pipes.Keys.Count];
					Pipes.Keys.CopyTo(keys, 0);

					foreach (int key in keys)
					{
						ServerNamedPipe serverPipe = (ServerNamedPipe)Pipes[key];

						if (serverPipe != null &&
							DateTime.Now.Subtract(serverPipe.LastAction).Milliseconds > MaxPipeStuffedTime &&
							serverPipe.PipeConnection.GetState() != InterProcessConnectionState.WaitingForClient)
						{
							serverPipe.Listen = false;
							serverPipe.PipeThread.Abort();
							RemoveServerChannel(serverPipe.PipeConnection.NativeHandle);
						}
					}

					if (numChannels <= NumberPipes)
					{
						ServerNamedPipe pipe = new ServerNamedPipe(PipeName, OutBufferSize, InBufferSize, MaxReadBytes, false);

						try
						{
							pipe.Connect();
							pipe.LastAction = DateTime.Now;
							Interlocked.Increment(ref numChannels);
							pipe.Start();
							Pipes.Add(pipe.PipeConnection.NativeHandle, pipe);
						}
						catch (InterProcessIOException)
						{
							RemoveServerChannel(pipe.PipeConnection.NativeHandle);
							pipe.Dispose();
						}
					}
					else
					{
						Mre.Reset();
						Mre.WaitOne(1000, false);
					}
				}
			}
			catch (Exception ex)
			{
				// Log exception
				Console.WriteLine(ex.ToString());
				throw;
			}
		}

		public void Stop()
		{
			Listen = false;
			Mre.Set();

			try
			{
				int[] keys = new int[Pipes.Keys.Count];
				Pipes.Keys.CopyTo(keys, 0);
				keys.ForEach((key) => { ((ServerNamedPipe)Pipes[key]).Listen = false; });

				int i = numChannels * 3;

				for (int j = 0; j < i; j++)
				{
					StopServerPipe();
				}

				Pipes.Clear();
				Mre.Close();
				Mre = null;
			}
			catch (Exception ex)
			{
				// Log exception
				Console.WriteLine(ex.ToString());
				throw;
			}
		}

		public void WakeUp()
		{
			if (Mre != null)
			{
				Mre.Set();
			}
		}

		private void StopServerPipe()
		{
			try
			{
				ClientPipeConnection pipe = new ClientPipeConnection(PipeName);
				if (pipe.TryConnect()) { pipe.Close(); }
			}
			catch (Exception ex)
			{
				// Log exception
				Console.WriteLine(ex.ToString());
				throw;
			}
		}

		public void RemoveServerChannel(object param)
		{
			int handle = (int)param;
			System.Threading.Interlocked.Decrement(ref numChannels);
			Pipes.Remove(handle);
			this.WakeUp();
		}
	}

	public sealed class ServerNamedPipe : IDisposable
	{
		internal Thread PipeThread;
		internal ServerPipeConnection PipeConnection;
		internal bool Listen = true;
		internal DateTime LastAction;
		private bool disposed = false;

		private void PipeListener()
		{
			CheckIfDisposed();
			try
			{
				Listen = PipeManager.Manager.Listen;

				while (Listen)
				{
					LastAction = DateTime.Now;
					string request = PipeConnection.Read();
					LastAction = DateTime.Now;

					PipeConnection.Write(
						(request.Trim() != "") ?
						PipeManager.Manager.HandleRequest(request) :
						"Error: bad request");

					LastAction = DateTime.Now;
					PipeConnection.Disconnect();
					if (Listen) { Connect(); }
					PipeManager.Manager.WakeUp();
				}
			}
			catch (ThreadAbortException) { }
			catch (ThreadStateException) { }
			catch (Exception ex)
			{
				// Log exception
				Console.WriteLine(ex.ToString());
				throw;
			}
			finally
			{
				Close();
			}
		}

		internal void Connect()
		{
			CheckIfDisposed();
			PipeConnection.Connect();
		}

		internal void Close()
		{
			CheckIfDisposed();
			this.Listen = false;
			PipeManager.Manager.RemoveServerChannel(this.PipeConnection.NativeHandle);
			this.Dispose();
		}

		internal void Start()
		{
			CheckIfDisposed();
			PipeThread.Start();
		}

		private void CheckIfDisposed()
		{
			if (this.disposed) { throw new ObjectDisposedException("ServerNamedPipe"); }
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		private void Dispose(bool disposing)
		{
			if (!this.disposed)
			{
				PipeConnection.Dispose();
				if (PipeThread != null)
				{
					try { PipeThread.Abort(); }
					catch (ThreadAbortException) { }
					catch (ThreadStateException) { }
					catch (Exception ex)
					{
						// Log exception
						Console.WriteLine(ex.ToString());
						throw;
					}
				}
			}
			disposed = true;
		}

		~ServerNamedPipe()
		{
			Dispose(false);
		}

		internal ServerNamedPipe(string name, uint outBuffer, uint inBuffer, int maxReadBytes, bool secure)
		{
			PipeConnection = new ServerPipeConnection(name, outBuffer, inBuffer, maxReadBytes, secure);
			PipeThread = new Thread(new ThreadStart(PipeListener))
			{
				IsBackground = true,
				Name = "Pipe Thread " + this.PipeConnection.NativeHandle.ToString()
			};
			LastAction = DateTime.Now;
		}
	}	
}

#endif
#endif